import java.util.InputMismatchException;
import java.util.Scanner;


public class MainEjercicio6 {

	public static void main(String [ ] args){
		// Inicializo jugadores
		Jugador j1 =  new Jugador (1,3,5); 
		Jugador j2 =  new Jugador (2,1,5); 
		
		// Ultimos pesos utilizados, para la opcion 2 del menu. Le doy un valor inicial.
		double[] pesosAnt = new double[]{0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0,5};
		
		// Cantidad de pruebas realizadas. Solo para ver si puede utilizarse la opcion 2 del menu
		int cantidadJuegos = 0;
		int num = 0;
		
		// Red neuronal para trabajar con refuerzos
		NeuralNetwork funcValoracion = null;
		
		
		// Acepto opcion de entranamiento
		while (true){
		    System.out.println("Ingrese una de las siguientes opciones");
		    System.out.println("1 - Entrenamiento con pesos con un jugador que juega de forma aleatoria");
		    System.out.println("2 - Entrenamiento con pesos con una version previa de si mismo");
		    System.out.println("3 - Entrenamiento con pesos contra un humano");
		    System.out.println("4 - Jugar contra un jugador que juega de forma aleatoria (usando pesos anteriores)");
		    System.out.println("5 - Jugar contra un humano (usando pesos anteriores)");
		    System.out.println("6 - Entrenamiento con refuerzo frente a jugador que juega de forma aleatoria");
		    System.out.println("7 - Entrenamiento con refuerzo frente a la ultima version entrenada con pesos");
		    System.out.println("8 - Jugar usando el aprendizaje por refuerzo contra un aleatorio");
		    System.out.println("9 - Jugar usando el aprendizaje por refuerzo contra la ultima version entrenada con pesos");
		    Scanner in = new Scanner(System.in);
	        num = 1;
	        boolean terminado = false;
	        while (!terminado) {
	            try {
	                num = in.nextInt();
	                if ((num <= 9) && (num >= 1)) {
	                	// Algunas opciones necesitan que ya se haya realizado entenamiento
	                	if ((cantidadJuegos == 0) && ((num == 2) || (num == 4)|| (num == 5)|| (num == 7)|| (num == 8)|| (num == 9))){
	                		System.out.println("Aun no se realizaron juegos. Elija otra opcion.");
	                	} else {
	                		if (((num == 8) || (num == 9)) && (funcValoracion == null)){
	                			System.out.println("Aun no se entreno el jugador con refuerzos. Elija otra opcion");
	                		}
	                		terminado = true;
	                	}
	                } else {
	                    System.out.println("Ingrese un indice valido");
	                }
	            } catch (InputMismatchException ex) {
	                System.out.println("Ingrese una opcion valida");
	                in = new Scanner(System.in);
	            }
	        }
	        
	        int cantidadIteraciones = 1000;
	        
	        switch (num){
	        case 1:
	        	// Jugador 1 juega entrenando
	        	j1 = new Jugador (1,3,5); 
	        	// Jugador 2 juega aleatoriamente
                        j2 = new Jugador (2,1,5);
                        cantidadIteraciones = 1000;
                break;
	        case 2:
	        	// Jugador 1 juega entrenando
	        	j1 = new Jugador (1,3,5);
	        	// Jugador 2 juega utilizando pesos anteriores
	        	j2 = new Jugador (2,4,5);
	        	j2.setPesos(pesosAnt);
	        	cantidadIteraciones = 1000;
	        	break;
	        case 3:
	        	// Jugador 1 juega entrenando
	        	j1 = new Jugador (1,3,5);
	        	// Jugador 2 es un humano
	        	j2 = new Jugador (2,2,5);
	        	cantidadIteraciones = 10;
	        	break;
	        case 4:
	        	// Jugador 1 juega con pesos anteriores
	        	j1 = new Jugador (1,4,5);
	        	j1.setPesos(pesosAnt);
	        	// Jugador 2 juega aleatoriamente
	        	j2 = new Jugador (2,1,5);
	        	cantidadIteraciones = 1000;
	        	break;
	        case 5:
	        	// Jugador 1 juega con pesos anteriores
	        	j1 = new Jugador (1,4,5);
	        	j1.setPesos(pesosAnt);
	        	// Jugador 2 es un humano
	        	j2 = new Jugador (2,2,5);
	        	cantidadIteraciones = 10;
	        	break;
	        case 6:
	        	// Jugador 1 juega entrendando por refuerzos
	        	j1 = new Jugador (1,5,5);
	        	// Jugador 2 juega aleatoriamente
                j2 = new Jugador (2,1,5);
                cantidadIteraciones = 1000;
                break;
	        case 7:
	        	// Jugador 1 juega entrendando por refuerzos
	        	j1 = new Jugador (1,5,5);
	        	// Jugador 2 juega con los ultimos pesos entrenados
                j2 = new Jugador (2,4,5);
                j2.setPesos(pesosAnt);
                cantidadIteraciones = 1000;
                break;
	        case 8:
	        	// Jugador 1 juega usando el refuerzo aprendido anteriormente
	        	j1 = new Jugador (1,6,5, j1.listaValoraciones);
	        	j1.funcValoracion = funcValoracion;
	        	// Jugador 2 juega aleatoriamente
                j2 = new Jugador (2,1,5);
                cantidadIteraciones = 1000;
                break;
	        case 9:
	        	// Jugador 1 juega usando el refuerzo aprendido anteriormente
	        	j1 = new Jugador (1,6,5, j1.listaValoraciones);
	        	j1.funcValoracion = funcValoracion;
	        	// Jugador 2 juega con los ultimos pesos entrenados
                j2 = new Jugador (2,4,5);
                j2.setPesos(pesosAnt);
                cantidadIteraciones = 1000;
                break;
	        }
	        cantidadJuegos++;
            int juegosEmpatadosGlobal = 0;
            int juegosGanador1Global = 0;
            int juegosGanador2Global = 0;

            int juegosEmpatadosLocal = 0;
            int juegosGanador1Local = 0;
            int juegosGanador2Local = 0;
                
            //Corremos 100 juegos
            for (int i=0; i<cantidadIteraciones; i++) {
                Tablero tablero = new Tablero();
                if ((num == 3) || (num == 5)){
                	tablero.imprimirTablero();
                }
                while (!tablero.juegoTerminado()){
                	// Si i es par empieza jugando el jugador 1
                    if ((i % 2) == 0){
                		j1.elegirMovimiento(tablero);
                    } else {
                    	j2.elegirMovimiento(tablero);
                    }
                    // Imprimo tablero solo si juega contra un humano
                    if ((num == 3) || (num == 5)){
                    	tablero.imprimirTablero();
                    }
                    
                    if (!tablero.juegoTerminado()){
                    	// Juega otro jugador
                        if ((i % 2) == 0){
                    		j2.elegirMovimiento(tablero);
                        } else {
                        	j1.elegirMovimiento(tablero);
                        }
                        // Imprimo tablero
                        if ((num == 3) || (num == 5)){
                        	tablero.imprimirTablero();
                        }
                    }

                }
                if (num == 3){
                	tablero.imprimirTablero();
                }
                

                
                //Identificar quien gana la partida y actualizar los contadores
                if (tablero.getGanador() == 0) {
                    juegosEmpatadosGlobal++;
                    juegosEmpatadosLocal++;                    
                }
                else if (tablero.getGanador() == 1) {
                    juegosGanador1Global++;
                    juegosGanador1Local++;

                }
                else if (tablero.getGanador() == 2) {
                    juegosGanador2Global++;
                    juegosGanador2Local++;
                }  
                
                boolean actualizarValoraciones = false;
                if ((num == 6) || (num == 7)){
                	actualizarValoraciones = true;
                }
                // Llamo para finalizar el juego
                j1.terminaJuego(tablero.getGanador(),actualizarValoraciones, tablero);
                
                /*if (i >0 && i % 10 == 0) {
                    if (juegosGanador1Local > 5) {
                        System.out.println("Resumen ganados cada 10 juegos. Numero: " +i+ " Ganados por jugador1: "+ 1);  
                    } 
                    else {
                        System.out.println("Resumen ganados cada 10 juegos. Numero: " +i+ " Ganados por jugador1: "+ 0);  
                    
                    }
                    juegosEmpatadosLocal = 0;
                    juegosGanador1Local = 0;
                    juegosGanador2Local = 0;                
                }*/

            }
            
            // Tomo los pesos solo si utilice un jugador con pesos, porque los de refuerzos no toman los pesos.
            if (num <= 5){
            	pesosAnt = j1.getPesos();
            }
            
            // Si estaba entrenando voy a crear la funcion de valoracion
            if ((num == 6) || (num == 7)){
            	 funcValoracion = entrenarFuncValoracion(j1.listaValoraciones);
            }
            //System.out.println("pesos ant: "+ pesosAnt[0] + ","+ pesosAnt[1] + ","+ pesosAnt[2] + ","+ pesosAnt[3] + ","+ pesosAnt[4] + ",");
            
            System.out.println("\n" + "Empates: " + juegosEmpatadosGlobal);
            System.out.println("Ganados por jugador 1: " + juegosGanador1Global + "  ----> " + (juegosGanador1Global*100/cantidadIteraciones) + "%");
            System.out.println("Ganados por jugador 2: " + juegosGanador2Global +  "  ----> " + (juegosGanador2Global*100/cantidadIteraciones) + "%\n");
                
		}
	}
	
	static NeuralNetwork entrenarFuncValoracion(ListaValoraciones listaValoraciones){

		double[][] entradas = new double [listaValoraciones.valoraciones.size()][6];
		double[][] salidas = new double [listaValoraciones.valoraciones.size()][1];
		
		//System.out.println("tamanio: " + listaValoraciones.valoraciones.size());
		
		// Completo las entradas
		for (int i = 0; i < listaValoraciones.valoraciones.size(); i++){
			//System.out.print("Entrada: (");
			for (int j = 0; j < 6; j++){
				entradas[i][j] = listaValoraciones.valoraciones.get(i).atributos[j];
				//System.out.print(entradas[i][j]);
			}
			//System.out.println("): " + listaValoraciones.valoraciones.get(i).valoracion);
		}
		
		for (int i = 0; i < listaValoraciones.valoraciones.size(); i++){
			salidas[i][0] = listaValoraciones.valoraciones.get(i).valoracion;
		}
		
		NeuralNetwork funcValoracion = new NeuralNetwork(5000, 48, listaValoraciones.valoraciones.size(), 1, 6);
		funcValoracion.learningSetInput = entradas;
		funcValoracion.learningSetOutput = salidas;
		
		funcValoracion.training();
		
		return funcValoracion;
	}
}
